package com.aruio.excel;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.EasyExcelFactory;
import com.alibaba.excel.annotation.ExcelIgnore;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.metadata.style.WriteFont;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import com.arui.exception.ServiceException;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.net.URLEncoder;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * @Description excel处理工具类
 * @Date 2024/9/3 9:23
 * @Created by 10574
 */
@Slf4j
public class ExcelUtils {

    private static final String XLS_CONTENT_TYPE = "application/vnd.ms-excel";
    private static final String XLSX_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
    private static final String CSV_CONTENT_TYPE = "text/csv";

    /**
     * 通用导出excel方法
     */
    public static void exportExcel(Class<?> clazz, List<?> excelData, String fileName, ExcelTypeEnum excelType, HttpServletResponse response) throws IOException {
        fileName = URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20") + excelType.getValue();
        switch (excelType) {
            case XLS:
                response.setContentType(XLS_CONTENT_TYPE);
                break;
            case XLSX:
                response.setContentType(XLSX_CONTENT_TYPE);
                break;
            case CSV:
                response.setContentType(CSV_CONTENT_TYPE);
                break;
        }
        response.setCharacterEncoding("utf-8");
        response.setHeader("Content-Disposition", "attachment;filename*=utf-8''" + fileName);
        final HorizontalCellStyleStrategy cellStyleStrategy = setCellStyle();
        EasyExcel.write(response.getOutputStream(), clazz).registerWriteHandler(cellStyleStrategy).excelType(excelType).sheet().doWrite(excelData);
    }

    /**
     * 设置excel单元格风格
     */
    public static HorizontalCellStyleStrategy setCellStyle() {
        final WriteCellStyle headStyle = new WriteCellStyle();
        //设置表头的样式 (背景颜色)
        headStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());

        //设置表头的样式 (字体占比，加粗)
        final WriteFont writeFont = new WriteFont();
        writeFont.setFontHeightInPoints((short) 12);
        writeFont.setBold(true);
        headStyle.setWriteFont(writeFont);

        //设置居中
        headStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);

        //设置excel内容策略(水平居中)
        final WriteCellStyle cellStyle = new WriteCellStyle();
        cellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
        return new HorizontalCellStyleStrategy(headStyle, cellStyle);
    }

    /*
     * 上传错误的Excel文件到OSS
     * */
    public static String exportExcelToOSS(Class<?> clazz, List<?> excelData, String fileName, ExcelTypeEnum excelType) throws IOException {
        HorizontalCellStyleStrategy styleStrategy = setCellStyle();
        fileName = fileName + excelType.getValue();
        File excelFile = File.createTempFile(fileName, excelType.getValue());
        EasyExcelFactory.write(excelFile, clazz).registerWriteHandler(styleStrategy).sheet().doWrite(excelData);
        return "";
    }

    /**
     * 总体思路:
     * 1. 拿到excel模板的头的信息, 再查看excel模板类上是否存在忽略字段
     * 2. 遍历模板类的字段，
     * 2.1 当字段上不存在ExcelProperty的时候，如果类上存在Ignore注解，则可以忽略该字段，如果不存在Ignore注解，则直接报错
     * 2.2 当字段存在ExcelProperty的时候，就获取对应的value，并比对是否在excel模板中存在, 存在则count++
     * 3. 校验excel模板和excel模板类的字段数量是否一直
     *
     * @param headMap excel模板头的字段Map key为头的index, value为头的具体名称
     * @param clazz   excel模板类
     * @param fields  excel模板类所包含的字段
     */
    public static <T> void validateExcelTemplate(Map<Integer, String> headMap, Class<T> clazz, Field[] fields) {
        final Collection<String> headValues = headMap.values();
        int count = 0;
        final boolean isClassExcelIgnore = clazz.isAnnotationPresent(ExcelIgnoreUnannotated.class);
        for (Field field : fields) {
            log.info("field:{}", field.getName());
        }
        for (Field field : fields) {
            if (field.isAnnotationPresent(ExcelIgnore.class)) {
                continue;
            }
            final ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class);
            if (Objects.isNull(excelProperty)) {
                if (isClassExcelIgnore) {
                    continue;
                }
                throw new ServiceException("请检查导入的excel文件是否按模板填写");
            }
            final String name = excelProperty.value()[0];
            if (!StringUtils.hasText(name) && !headValues.contains(field.getName())) {
                throw new ServiceException("请检查导入的excel文件是否按模板填写");
            }
            if (StringUtils.hasText(name) && !headValues.contains(name)) {
                throw new ServiceException("请检查导入的excel文件是否按模板填写");
            }
            count++;
        }
        if (headValues.size() != count) {
            throw new ServiceException("请检查导入的excel文件是否按模板填写");
        }
    }


}
